/***
 * * PROJECT
 * *    ATaG
 * * VERSION
 * *    $Revision: 1.3 $
 * * DATE
 * *    $Date: 2007/06/17 11:09:01 $
 * * LOG
 * *    $Log: DataPool.java,v $
 * *    Revision 1.3  2007/06/17 11:09:01  animesh
 * *    Serialization done. New Sensor and Actuator classes added.
 * *
 * *    Revision 1.2  2007/06/13 00:43:31  animesh
 * *    no message
 * *
 * *    Revision 1.10  2007/01/04 09:14:08  lmottola
 * *    Changed constructor for AtagManager.
 * *
 * *    Revision 1.9  2006/12/20 23:05:07  lmottola
 * *    Removed reference to ATaGProgram in NodeInfo.
 * *
 * *    Revision 1.8  2006/11/03 23:08:17  lmottola
 * *    Traffic application beta running.
 * *
 * *    Revision 1.7  2006/10/27 01:28:28  lmottola
 * *    Debugging support added.
 * *
 * *    Revision 1.6  2006/10/26 23:58:31  lmottola
 * *    Bug fixes: Hello World application running in SWANS.
 * *
 * *    Revision 1.5  2006/10/25 22:34:16  animesh
 * *    changes the myLocation call to getMyLocation
 * *
 * *    Revision 1.4  2006/10/20 21:54:37  lmottola
 * *    Rmoved import.
 * *
 * *    Revision 1.3  2006/10/19 21:21:52  lmottola
 * *    Added CVS preamble.
 * *
 * *
 * *   ATaG (Abstract Task Graph) - Data-driven Macroprogramming for
 * *                                Sensor Networks
 * *
 * *   This program is free software; you can redistribute it and/or
 * *   modify it under the terms of the GNU General Public License
 * *   as published by the Free Software Foundation; either version 2
 * *   of the License, or (at your option) any later version.
 * *
 * *   This program is distributed in the hope that it will be useful,
 * *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 * *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * *   GNU General Public License for more details.
 * *
 * *   You should have received a copy of the GNU General Public License
 * *   along with this program; if not, you may find a copy at the FSF web
 * *   site at 'www.gnu.org' or 'www.fsf.org', or you may write to the
 * *   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * *   Boston, MA  02111-1307, USA
 ***/

package atag.runtime;

import java.io.IOException;
import java.util.Vector;

import atag.runtime.ln.neighborhoodDefs.Neighborhood;
import atag.runtime.ln.LogicalNeighborhoods;
import atag.runtime.ln.LNDeliver;
import atag.runtime.ln.neighborhoodDefs.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;

public class DataPool implements LNDeliver{

	private Vector m_dataPool = new Vector();

	private AtagManager m_AtagManager;

	private NodeInfo m_config;

	private LogicalNeighborhoods ln;

	public DataPool(LogicalNeighborhoods ln, NodeInfo t_config,
			AtagManager atagManager) {
		this.m_config = t_config;
		this.ln = ln;
		this.m_AtagManager = atagManager;
                //this.m_dataPool new Vector(m_AtagManager.getAtagPrg().numDataItems());
		DataItem dummy = new DataItem(-1, -1, null);
		ln.setReceiver(this);
		atagManager.setDataPool(this);
                // TODO - the line below is wasteful, as it creates an entry for
                // all data items, irrespective of them being handled at this node. Not 
                // good for large programs
		for (int i = 0; i < m_AtagManager.getAtagPrg().numDataItems(); i++) {
			m_dataPool.addElement(dummy);
                        //m_dataPool.add(dummy,i);
		}
	}

	public synchronized DataItem getData(int taskID, int dataID) {
		DataItem t_dataItem = (DataItem) m_dataPool.elementAt(dataID);
		if (t_dataItem.isAlive(taskID)) {
			t_dataItem.decrementRef(taskID);
			return t_dataItem;
		} else
			return null;
	}

	public synchronized boolean putData(DataItem outputData) {

		int t_dataID = outputData.dataID();
		// If data item is active, do nothing and return "false"
		if (m_dataPool.elementAt(t_dataID) != null) {
			if (((DataItem) m_dataPool.elementAt(t_dataID)).isActive())
				return false;
		}

		// Set time, date, node ID, and node coordinates of origin
		outputData.setDateStamp(m_config.getClock().getDate());
		outputData.setTimeStamp(m_config.getClock().getTime());
		outputData.setOriginID(m_config.getMyId());
		outputData.setOriginLocation(m_config.getMyLocation());
		// Add this to the datapool before calling AtagManager
		m_dataPool.setElementAt(outputData, t_dataID);
		// Call AtagManager who will set references and will spawn tasks
		//m_AtagManager.newInstanceProduced(outputData.producerID(), outputData
		//		.dataID());

		Neighborhood[] destinationScopes = this.m_AtagManager.getLNScopeForData(
				outputData.producerID(), t_dataID);

		if (!(destinationScopes == null)) {
			// The data needs to go somewhere non-local
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            DataOutputStream dg = new DataOutputStream(bout);
            
            try {
                outputData.serialize(dg);
            } catch (IOException ex) {
                ex.printStackTrace();
            }
			this.ln.send(bout.toByteArray(), destinationScopes);
		}

		return true;
	}

        public synchronized boolean putData(DataItem outputData,String partitionLabel,int partitionValue) {

		int t_dataID = outputData.dataID();
		// If data item is active, do nothing and return "false"
		if (m_dataPool.elementAt(t_dataID) != null) {
			if (((DataItem) m_dataPool.elementAt(t_dataID)).isActive())
				return false;
		}

		// Set time, date, node ID, and node coordinates of origin
		outputData.setDateStamp(m_config.getClock().getDate());
		outputData.setTimeStamp(m_config.getClock().getTime());
		outputData.setOriginID(m_config.getMyId());
		outputData.setOriginLocation(m_config.getMyLocation());
		// Add this to the datapool before calling AtagManager
		m_dataPool.setElementAt(outputData, t_dataID);
		// Call AtagManager who will set references and will spawn tasks
		//m_AtagManager.newInstanceProduced(outputData.producerID(), outputData
		//		.dataID());

		Neighborhood[] destinationScopes = this.m_AtagManager.getLNScopeForData(
				outputData.producerID(), t_dataID,partitionLabel,partitionValue);

		if (!(destinationScopes == null)) {
			// The data needs to go somewhere non-local
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            DataOutputStream dg = new DataOutputStream(bout);

            try {
                outputData.serialize(dg);
            } catch (IOException ex) {
                ex.printStackTrace();
            }
			this.ln.send(bout.toByteArray(), destinationScopes);
		}

		return true;
	}

        public synchronized boolean putData(DataItem outputData,int nodeID) {

		int t_dataID = outputData.dataID();
		// If data item is active, do nothing and return "false"
		if (m_dataPool.elementAt(t_dataID) != null) {
			if (((DataItem) m_dataPool.elementAt(t_dataID)).isActive())
				return false;
		}

		// Set time, date, node ID, and node coordinates of origin
		outputData.setDateStamp(m_config.getClock().getDate());
		outputData.setTimeStamp(m_config.getClock().getTime());
		outputData.setOriginID(m_config.getMyId());
		outputData.setOriginLocation(m_config.getMyLocation());
		// Add this to the datapool before calling AtagManager
		m_dataPool.setElementAt(outputData, t_dataID);
		// Call AtagManager who will set references and will spawn tasks
		//m_AtagManager.newInstanceProduced(outputData.producerID(), outputData
		//		.dataID());

                Predicate[] nodePred = new Predicate[1];
                
                nodePred[0] = new IntegerSimplePredicate
                        ("NodeID", IntegerSimplePredicate.EQUAL, nodeID);			
                
		Neighborhood[] destinationScopes = 
                    {new ConjunctiveNeighborhood(nodePred) };
                
		if (!(destinationScopes == null)) {
			// The data needs to go somewhere non-local
                    ByteArrayOutputStream bout = new ByteArrayOutputStream();
                    DataOutputStream dg = new DataOutputStream(bout);

                try {
                    outputData.serialize(dg);
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
                this.ln.send(bout.toByteArray(), destinationScopes);
		}

		return true;
	}


	public synchronized boolean putDataFromNetwork(DataItem outputData) {

		int t_dataID = outputData.dataID();
		// If data item is present and is active, do nothing and return "false"
		if (m_dataPool.elementAt(t_dataID) != null)
			if (((DataItem) m_dataPool.elementAt(t_dataID)).isActive()){
                                m_config.debugPrint("This data is already active. Not delivering again");
				return false;
                        }
		// If data originates from network, it is already stamped and should not
		// be changed
		// Add this to the datapool before calling AtagManager (is this ordering
		// OK?)
		m_dataPool.setElementAt(outputData,t_dataID);
		// System.err.println("Received data ID: " + t_dataID + " from node: " +
		// outputData.originID());
		// Call AtagManager who will set references and will spawn tasks
		m_AtagManager.newInstanceProduced(outputData.producerID(), outputData
				.dataID());
		return true;
	}

	public boolean isDataReady(int taskID, int dataID) {
		return (((DataItem) m_dataPool.elementAt(dataID)).isAlive(taskID));
	}

	public void setTotalRefs(int[] totalRefs, int dataID) {
		((DataItem) m_dataPool.elementAt(dataID)).setTotalReferences(totalRefs);
	}

	public void setNowRefs(int[] nowRefs, int dataID) {
		((DataItem) m_dataPool.elementAt(dataID)).setNowReferences(nowRefs);
	}

	public boolean isDataReady(int taskID, int[] dataIDs) {
		for (int idx = 0; idx < dataIDs.length; idx++)
			if (!((DataItem) m_dataPool.elementAt(dataIDs[idx])).isAlive(taskID))
				return false;
		return true;
	}

    public void deliver(byte[] data) {
        DataItem dataItem = new DataItem();
        ByteArrayInputStream bin= new ByteArrayInputStream(data);
        DataInputStream din = new DataInputStream(bin);
        try {
            dataItem.deserialize(din);
        } catch (IOException ex) {
            ex.printStackTrace();
        } catch (ClassNotFoundException ex) {
            ex.printStackTrace();
        } catch (IllegalAccessException ex) {
            ex.printStackTrace();
        } catch (InstantiationException ex) {
            ex.printStackTrace();
        }
        this.putDataFromNetwork(dataItem);
     }

}
